package es.upv.tricia.rest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import android.os.AsyncTask;

public class RestClient
{
	private static class WorkerTask
	{
		private HttpUriRequest request;
		private AsyncHttpResponseHandler responseHandler;

		public WorkerTask(HttpUriRequest request,
				AsyncHttpResponseHandler responseHandler)
		{
			this.request = request;
			this.responseHandler = responseHandler;
		}

		public HttpUriRequest getRequest()
		{
			return request;
		}

		public AsyncHttpResponseHandler getResponseHandler()
		{
			return responseHandler;
		}
	}

	private abstract static class WorkerProgress
	{
		private AsyncHttpResponseHandler responseHandler;

		public WorkerProgress(AsyncHttpResponseHandler responseHandler)
		{
			this.responseHandler = responseHandler;
		}

		public AsyncHttpResponseHandler getResponseHandler()
		{
			return responseHandler;
		}
	}

	private static class WorkerStart extends WorkerProgress
	{

		public WorkerStart(AsyncHttpResponseHandler responseHandler)
		{
			super(responseHandler);
		}
	}

	private static class WorkerSuccess extends WorkerProgress
	{
		private String response;

		public WorkerSuccess(AsyncHttpResponseHandler responseHandler,
				String response)
		{
			super(responseHandler);

			this.response = response;
		}

		public String getResponse()
		{
			return response;
		}
	}

	private static class WorkerFailure extends WorkerProgress
	{
		private Throwable throwable;

		public WorkerFailure(AsyncHttpResponseHandler responseHandler,
				Throwable t)
		{
			super(responseHandler);

			this.throwable = t;
		}

		public Throwable getThrowable()
		{
			return throwable;
		}
	}

	private static class WorkerFinish extends WorkerProgress
	{

		public WorkerFinish(AsyncHttpResponseHandler listener)
		{
			super(listener);
		}
	}

	private static class Worker extends
			AsyncTask<WorkerTask, WorkerProgress, Void>
	{

		private static HttpParams sHttpParams = new BasicHttpParams();

		static {
			HttpConnectionParams.setConnectionTimeout(sHttpParams, 2500);
			HttpConnectionParams.setSoTimeout(sHttpParams, 4500);
		}

		private HttpClient client = new DefaultHttpClient(sHttpParams);

		@Override
		protected Void doInBackground(WorkerTask... tasks)
		{
			WorkerProgress progress;

			for (WorkerTask task : tasks) {
				try {
					// start
					progress = new WorkerStart(task.getResponseHandler());
					publishProgress(progress);

					HttpResponse response = client.execute(task.getRequest());
					int statusCode = response.getStatusLine().getStatusCode();
					HttpEntity entity = response.getEntity();
					StringBuilder builder = new StringBuilder();

					if (entity != null) {
						InputStream is = entity.getContent();
						BufferedReader reader = new BufferedReader(
								new InputStreamReader(is));
						String line;

						while ((line = reader.readLine()) != null) {
							builder.append(line);
						}

						is.close();
						reader.close();
					}

					String responseText = builder.length() > 0 ? builder
							.toString() : null;

					if (statusCode >= 200 && statusCode < 300) {
						// success
						progress = new WorkerSuccess(task.getResponseHandler(),
								responseText);
						publishProgress(progress);
					} else {
						// failure
						progress = new WorkerFailure(task.getResponseHandler(),
								new RuntimeException(responseText));
						publishProgress(progress);
					}
				} catch (ClientProtocolException e) {
					progress = new WorkerFailure(task.getResponseHandler(), e);
					publishProgress(progress);
					e.printStackTrace();
				} catch (IOException e) {
					progress = new WorkerFailure(task.getResponseHandler(), e);
					publishProgress(progress);
					e.printStackTrace();
				} finally {
					progress = new WorkerFinish(task.getResponseHandler());
					publishProgress(progress);
				}
			}

			return null;
		}

		@Override
		protected void onProgressUpdate(WorkerProgress... progresses)
		{
			for (WorkerProgress progress : progresses) {
				if (progress.getResponseHandler() != null) {
					if (progress instanceof WorkerStart) {
						progress.getResponseHandler().onStart();
					} else if (progress instanceof WorkerSuccess) {
						WorkerSuccess success = (WorkerSuccess) progress;
						progress.getResponseHandler().onSuccess(
								success.getResponse());
					} else if (progress instanceof WorkerFailure) {
						WorkerFailure failure = (WorkerFailure) progress;
						progress.getResponseHandler().onFailure(
								failure.getThrowable());
					} else if (progress instanceof WorkerFinish) {
						progress.getResponseHandler().onFinish();
					}
				}
			}
		}
	}

	private static final String BASE_URL = "http://dakkon.disca.upv.es:8080/WebServiceTrivial/rest/";

	public static void get(String url, AsyncHttpResponseHandler responseHandler)
	{
		HttpGet request = new HttpGet(getAbsoluteUrl(url));

		new Worker().execute(new WorkerTask(request, responseHandler));
	}

	public static void get(String url, RequestParams params,
			AsyncHttpResponseHandler responseHandler)
	{
		if (params.getPairs().size() > 0) {
			url += "?" + URLEncodedUtils.format(params.getPairs(), "utf-8");
		}

		get(url, responseHandler);
	}

	public static void delete(String url,
			AsyncHttpResponseHandler responseHandler)
	{
		HttpDelete request = new HttpDelete(getAbsoluteUrl(url));

		new Worker().execute(new WorkerTask(request, responseHandler));
	}

	public static void delete(String url, RequestParams params,
			AsyncHttpResponseHandler responseHandler)
	{
		if (params.getPairs().size() > 0) {
			url += "?" + URLEncodedUtils.format(params.getPairs(), "utf-8");
		}

		delete(url, responseHandler);
	}

	public static void post(String url, RequestParams params,
			AsyncHttpResponseHandler responseHandler)
	{
		try {
			HttpPost request = new HttpPost(getAbsoluteUrl(url));

			if (params.getPairs().size() > 0) {
				request.setEntity(new UrlEncodedFormEntity(params.getPairs()));
			}

			new Worker().execute(new WorkerTask(request, responseHandler));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}

	public static void put(String url, RequestParams params,
			AsyncHttpResponseHandler responseHandler)
	{
		try {
			HttpPut request = new HttpPut(getAbsoluteUrl(url));

			if (params.getPairs().size() > 0) {
				request.setEntity(new UrlEncodedFormEntity(params.getPairs()));
			}

			new Worker().execute(new WorkerTask(request, responseHandler));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}

	private static String getAbsoluteUrl(String relativeUrl)
	{
		return BASE_URL + relativeUrl;
	}
}